package com.yesep.learn.jwt.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.yesep.learn.jwt.filter.JWTAuthenticationFilter;
import com.yesep.learn.jwt.filter.JWTLoginFilter;
import com.yesep.learn.jwt.handler.AccessDeniedHandlerImpl;
import com.yesep.learn.jwt.handler.AuthenticationEntryPointImpl;
import com.yesep.learn.jwt.handler.AuthenticationFailureHandlerImpl;
import com.yesep.learn.jwt.handler.AuthenticationSuccessHandlerImpl;
import com.yesep.learn.jwt.handler.LogoutSuccessHandlerImpl;

import lombok.extern.slf4j.Slf4j;

/**
 * Security 核心配置类
 */
@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private JWTTokenConfig jwtTokenConfig;

	@Autowired
	private UserDetailsService userDetailsService;

	@Bean
	public AuthenticationSuccessHandler authenticationSuccessHandler() {
		return new AuthenticationSuccessHandlerImpl();
	}

	@Bean
	public AuthenticationFailureHandler authenticationFailureHandler() {
		return new AuthenticationFailureHandlerImpl();
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		//System.out.println(passwordEncoder().encode("ffffff"));
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

		ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();

		// 除配置文件忽略路径其它所有请求都需经过认证和授权
		if (null != jwtTokenConfig.getWhitelist()) {
			for (String url : jwtTokenConfig.getWhitelist()) {
				registry.antMatchers(url).permitAll();
			}
		}
		registry.antMatchers(HttpMethod.GET, "/", "/*.html", "/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js", "/webjars/springfox-swagger-ui/images/**", "/swagger-resources/configuration/*", "/swagger-resources", // swagger请求
				"/v2/api-docs").permitAll();

		registry.antMatchers(HttpMethod.OPTIONS).permitAll();

		// 表单登录方式
		http.cors().and().formLogin()
				// 登录提醒页面
				.loginPage(jwtTokenConfig.getLoginPage())
				// 登录请求处理URL
				.loginProcessingUrl(jwtTokenConfig.getLoginProcessingUrl()).permitAll()
				// 登录成功后 AuthenticationSuccessHandler类中onAuthenticationSuccess（）被调用
				.successHandler(authenticationSuccessHandler())
				// 登录失败后 AuthenticationFailureHandler 类中onAuthenticationFailure（）被调用
				.failureHandler(authenticationFailureHandler())
				// 允许网页iframe
				.and().headers().frameOptions().disable()
				// 退出业务处理,无需授权
				.and().logout().logoutUrl(jwtTokenConfig.getLogoutUrl()).logoutSuccessHandler(new LogoutSuccessHandlerImpl(jwtTokenConfig)).permitAll()
				// 其它任何请求需要身份认证
				.and().authorizeRequests().anyRequest().authenticated()
				// 关闭跨站请求防护
				.and().csrf().disable()
				// 前后端分离采用JWT 不需要session
				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
				// 自定义权限拒绝处理类
				// 用户已经通过了登录认证，在访问一个受保护的资源，但是权限不够，则抛出授权异常，AccessDeniedHandler类中handle()就会调用
				// 认证配置当用户请求了一个受保护的资源，但是用户没有通过登录认证，则抛出登录认证异常，AuthenticationEntryPointHandler类中commence()就会调用
				.and().exceptionHandling().accessDeniedHandler(new AccessDeniedHandlerImpl()).authenticationEntryPoint(new AuthenticationEntryPointImpl())
				// 添加JWT登录处理拦截
				.and().addFilter(new JWTLoginFilter(authenticationManager(), authenticationSuccessHandler(), authenticationFailureHandler(), jwtTokenConfig.getLoginProcessingUrl()))
				// 添加JWT认证过滤器 除/auth/token其它请求都需经过此过滤器
				.addFilterBefore(new JWTAuthenticationFilter(authenticationManager(), jwtTokenConfig), UsernamePasswordAuthenticationFilter.class);
	}

}
